/*
 *    Copyright (c) 2014 Xiang Xu <xuxiang@mail.bnu.edu.cn>
 *
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 *
 *    The above copyright notice and this permission notice shall be
 *    included in all copies or substantial portions of the Software.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef FACADE_GEOGRAPHY_H
#define FACADE_GEOGRAPHY_H

#include "pcl/point_types.h"
#include "pcl/point_cloud.h"
#include "opencv2/opencv.hpp"
#include "boost/smart_ptr.hpp"

#include <iostream>
#include <string>
#include <vector>
#include <map>

class FacadeGrammar;

class FacadeFeature
{
public:
    //! features of facade
    enum FeatureType
    {
        DENSITY = 0, DEPTH = 1, CURVATE = 2,
        FEATURE_NUMBER
    };

    /** /brief Constructor
     * \param width Facade width.
     * \param height Facade Height.
     * \param facade_grammar Facade grammar.
    */
    FacadeFeature(int width, int height, boost::shared_ptr<FacadeGrammar> facade_grammar);
    FacadeFeature(boost::shared_ptr<FacadeGrammar> facade_grammar);
    void init(int width, int height, boost::shared_ptr<FacadeGrammar> facade_grammar);

    ~FacadeFeature();
    void destroy();

    /** /brief Load grid spatial extend from file
     */
    void LoadGridScale(const char* filename);

    /** /brief Create the facade grid/features with pcl point cloud.
     * \param facade_cloud facade point cloud.
     * \param resulution rasterize resolution
     */
    void CreateGridFromPointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud, double resolution);

    /** /brief Normalize features
     */
    void NormalizeFeatures();

    //! Render features to picture.
    void RenderGrds(std::string& path);

    //! Save features to file
    void SaveGrids(std::string& filename);

    //! Load features from file
    void LoadGrids(std::string& filename);

    //! Set the value at a specified pixel(grid).
    /*!
    \param width the width of the pixel.
    \param height the height of the pixel
    \param depth the depth of the pixel.
    \param value the value that we want to set in this pixel.
    */
    void SetProperty(int width, int height, int depth, double value);

    //! get the value at a specified pixel(grid).
    /*!
    \param width the width of the pixel.
    \param height the height of the pixel
    \param depth the depth of the pixel.
    \return the value in this pixel.
    */
    double GetProperty(int width, int height, int depth);

    //! get the value at a specified position.
    /*!
    \param pos the position of the pixel.
    \param depth the depth of the pixel.
    \return the value in this pixel.
    */
    double GetProperty(const cv::Point2d &pos, int depth);

    std::vector<int> FindLoacalMinimalAndMaximal(std::vector<double>& feature);
    std::vector<double> Smooth(std::vector<double> feature, int left, int right);
    void SaveParameters(std::string parater_path);
    void CalculateParameters(std::string path);

    // Inline functions
    inline double get_resolution() const
    {
        return resolution_;
    }
    inline int get_width() const
    {
        return width_;
    }
    inline int get_height() const
    {
        return height_;
    }
    inline int get_depth() const
    {
        return depth_;
    }
    inline cv::Point3d get_extents_min()
    {
        return extents_min_;
    }
    inline cv::Point3d get_extents_range()
    {
        return extents_range_;
    }
    inline cv::Point2d get_convert_ratio()
    {
        return convert_ratio_;
    }

    // dangerous access
    inline double** get_grd_ptr(int index)
    {
        return grd_[index];
    }

    // for tests
    void SetSymbol(double xmin, double xmax, double ymin, double ymax, int symbol);

protected:
    std::vector<double**> grd_;  /*!< Facade grid. */
    boost::shared_ptr<FacadeGrammar> facade_grammar_; /*!< Facade grammar. */

    cv::Point3d extents_min_, extents_range_;
    cv::Point2d convert_ratio_; /*!< Facade extent and convert ratio. */

    double resolution_; /*!< Facade resolution. */
    int width_, height_, depth_; /*!< Facade grid extent and depth. */

    std::vector<std::vector<double> > vertical_features, horizonal_features;
    std::vector<std::vector<int> > action_parameters_vertical_, action_parameters_horizontal_;
};

#endif // FACADE_GEOGRAPHY_H
